package com.lucifer.d.security.service.local.aspect;

import com.lucifer.d.security.annotation.model.Role;
import com.lucifer.d.security.annotation.model.RoleId;
import com.lucifer.d.security.annotation.model.RoleMap;
import com.lucifer.d.security.annotation.model.RoleName;
import com.lucifer.d.security.annotation.verify.Auth;
import com.lucifer.d.security.annotation.verify.Space;
import com.lucifer.d.security.model.SecurityConfigModel;
import com.lucifer.d.security.service.gateway.properties.BootConfigProperties;
import com.lucifer.d.security.exception.AuthException;
import com.lucifer.d.security.exception.TokenException;
import com.lucifer.d.security.transfer.UserTransfer;
import com.lucifer.d.security.util.ReflectUtil;
import com.lucifer.d.util.StringUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;

import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;

/**
 * 鉴权过滤
 *
 * @author Lucifer
 */
@Aspect
public class AuthAspect {

    @Autowired
    private UserTransfer userTransfer;

    @Autowired
    private BootConfigProperties bootConfigProperties;

    /**
     * 定义切入点，为所有service
     */
    @Pointcut("@annotation(com.lucifer.d.security.annotation.verify.Auth)")
    public void authorityAspect() {}

    /**
     * 切入点前置执行内容
     *
     * @param point 切入点对象映射
     * @throws Throwable 异常
     */
    @Around("authorityAspect()")
    public Object doBefore(ProceedingJoinPoint point) throws Throwable {
        // 网关过滤模式不进行业务拦截
        if (!bootConfigProperties.isEnable()){
            // 获取用户信息
            Object securityUser = userTransfer.getSecurityUser();
            if (securityUser == null){
                throw new TokenException(TokenException.CODE302);
            }
            // 获取切入类名，反射获取对应类
            Class<?> clazz = point.getTarget().getClass();
            // 判断是否定义范围拦截
            Space space = clazz.getAnnotation(Space.class);
            // 验证标识
            Boolean verify = false;
            // 用户验证角色Id
            Object roleId = ReflectUtil.getObjFieldOnReflect(securityUser, RoleId.class);
            // 用户验证角色名称
            Object roleName = ReflectUtil.getObjFieldOnReflect(securityUser, RoleName.class);
            // 用户验证角色信息载体
            Object roleMessage = ReflectUtil.getObjFieldOnReflect(securityUser, Role.class);
            // 若信息载体不存在角色id属性，则到角色信息载体内获取
            if (roleId == null){
                roleId = ReflectUtil.getObjFieldOnReflect(roleMessage, RoleId.class);
            }
            // 若信息载体不存在角色名称属性，则到角色信息载体内获取
            if (roleName == null){
                roleName = ReflectUtil.getObjFieldOnReflect(roleMessage, RoleName.class);
            }
            // FIXME 不存在角色id视为权限异常
            if (roleId == null){
                throw new AuthException();
            }
            // FIXME 系统调用直接放行（双验证）
            if (passVerify(String.valueOf(roleId),String.valueOf(roleName))){
                verify = true;
            }else {
                // FIXME 如果未标记权限过滤，直接放行
                if (space != null){
                    // 权限存储容器
                    List<String> permissions = null;
                    Object authMapObj = ReflectUtil.getObjFieldOnReflect(roleMessage, RoleMap.class);
                    if (authMapObj == null){
                        throw new AuthException("用户不存在对应角色/权限信息");
                    }
                    Map<String,Object> authMapOnAnalysis = (Map<String, Object>) authMapObj;
                    for (String menu : authMapOnAnalysis.keySet()){
                        if (menu.equals(space.value())){
                            permissions = (List<String>) authMapOnAnalysis.get(menu);
                        }
                    }
                    if (permissions == null){
                        throw new AuthException("账户无权限");
                    }
                    // 获取类方法
                    Method[] methods = clazz.getDeclaredMethods();
                    // 方法对应
                    Boolean hasMethod = false;
                    x: for (Method method : methods) {
                        // 根据切入点获取方法名，和类方法对比，拿到切入方法，
                        if (method.getName().equals(point.getSignature().getName())) {
                            hasMethod = true;
                            // 获取该业务需要权限注解
                            Auth auth = method.getAnnotation(Auth.class);
                            if (auth != null){
                                if (!auth.value().equals("get")){
                                    for (String permission : permissions){
                                        if (permission.equals(auth.value())){
                                            verify = true;
                                            break x;
                                        }
                                    }
                                }else {
                                    verify = true;
                                    break x;
                                }
                            }
                        }
                    }
                    if (!hasMethod){
                        throw new AuthException("未找到对应权限验证业务方法");
                    }
                }else {
                    verify = true;
                }
            }
            if (!verify){
                throw new AuthException("账户无权限");
            }
            return point.proceed();
        }
        return point.proceed();
    }

    /**
     * 直接通行验证
     *
     * @param roleId
     * @param roleName
     * @return
     */
    private Boolean passVerify(String roleId, String roleName){
        SecurityConfigModel securityConfigModel = userTransfer.getSecurityConfigModel();;
        Boolean result = false;
        if (!StringUtil.isEmpty(securityConfigModel.getPassRoleId())){
            if (securityConfigModel.getPassRoleId().equals(roleId)){
                result = true;
            }
        }
        if (!StringUtil.isEmpty(securityConfigModel.getPassRoleName())){
            if (securityConfigModel.getPassRoleName().equals(roleName)){
                result = true;
            }
        }
        return result;
    }

}
